#pragma once

#include <tchar.h>
#include <gtl/io/file.h>
#include <gtl/type/inner.h>
#include <gtl/string/str.h>

namespace gtl
{

class archive
{
public:
	archive()
	{
	}

public:
	bool open(const gtl::tchar* path, bool load = false)
	{
		if(!m_file.open(path, _T("r+b")))
			return m_file.open(path, _T("w+b"));

		return true;
	}

	void close()
	{
		m_file.close();
	}

	bool eof()
	{
		return m_file.eof();
	}

	__int64 tell()
	{
		return m_file.tell();
	}

	int seek(__int64 offset, int mode)
	{
		return m_file.seek(offset, mode);
	}

public:
	archive& operator<<(char val)
	{
		m_file.write(val);
		return* this;
	}

	archive& operator>>(char& val)
	{
		m_file.read(val);
		return* this;
	}

	archive& operator<<(unsigned char val)
	{
		m_file.write(val);
		return* this;
	}

	archive& operator>>(unsigned char& val)
	{
		m_file.read(val);
		return* this;
	}

	archive& operator<<(short val)
	{
		m_file.write(val);
		return* this;
	}

	archive& operator>>(short& val)
	{
		m_file.read(val);
		return* this;
	}

	archive& operator<<(unsigned short val)
	{
		m_file.write(val);
		return* this;
	}

	archive& operator>>(unsigned short& val)
	{
		m_file.read(val);
		return* this;
	}

	archive& operator<<(int val)
	{
		m_file.write(val);
		return* this;
	}

	archive& operator>>(int& val)
	{
		m_file.read(val);
		return* this;
	}

	archive& operator<<(unsigned int val)
	{
		m_file.write(val);
		return* this;
	}

	archive& operator>>(unsigned int& val)
	{
		m_file.read(val);
		return* this;
	}

	archive& operator<<(long val)
	{
		m_file.write(val);
		return* this;
	}

	archive& operator>>(long& val)
	{
		m_file.read(val);
		return* this;
	}

	archive& operator<<(unsigned long val)
	{
		m_file.write(val);
		return* this;
	}

	archive& operator>>(unsigned long& val)
	{
		m_file.read(val);
		return* this;
	}

	archive& operator<<(__int64 val)
	{
		m_file.write(val);
		return* this;
	}

	archive& operator>>(__int64& val)
	{
		m_file.read(val);
		return* this;
	}

	archive& operator<<(unsigned __int64 val)
	{
		m_file.write(val);
		return* this;
	}

	archive& operator>>(unsigned __int64& val)
	{
		m_file.read(val);
		return* this;
	}

	template<class TChar>
	archive& operator<<(const gtl::str_t<TChar>& str)
	{
		*this << (unsigned short)str.size();
		if(str.empty())
			return *this;

		m_file.write(str.c_str(), str.size());
		return *this;
	}

	template<class TChar>
	archive& operator>>(gtl::str_t<TChar>& str)
	{
		unsigned short size = 0;
		*this >> size;
		if(size == 0)
		{
			str.clear();
			return *this;
		}

		str.resize(size + 1);
		m_file.read(str.data(), size);
		return *this;
	}

	// vector, deque, list ect.
	template<class T, template<class, class> class Container>
	archive& operator<<(const Container<T, std::allocator<T> >& c)
	{
		typedef Container<T, std::allocator<T> > container_type;

		*this << c.size();
		container_type::const_iterator ite = c.begin();
		for(; ite != c.end(); ++ite)
		{
			*this << *ite;
		}

		return *this;
	}

	template<class T, template<class, class> class Container>
	archive& operator>>(Container<T, std::allocator<T> >& c)
	{
		typedef Container<T, allocator<T> > container_type;

		size_t size = 0;
		ar >> size;
		if(size <= 0)
			return ar;

		c.resize(size);
		container_type::iterator ite = c.begin();
		for(; ite != c.end(); ++ite)
		{
			ar >> (*ite);
		}

		return ar;
	}

protected:
	file m_file;
};

class iarchive : public archive 
{
public:
	iarchive()
	{
	}

	iarchive(const gtl::tchar* path)
	{
		open(path, false);
	}

public:
	using archive::operator >>;

	template<class T>
	iarchive& operator>>(T& val)
	{
		val.serialize(*this);
		return *this;
	}

	template<class T>
	iarchive& operator&(T& val)
	{
		operator>>(val);
		return *this;
	}

private:
	template<class T>
	iarchive& operator<<(T& val);
};

class oarchive : public archive
{
public:
	oarchive()
	{
	}

	oarchive(const gtl::tchar* path)
	{
		open(path, true);
	}

public:
	using archive::operator <<;

	template<class T>
	oarchive& operator<<(T& val)
	{
		val.serialize(*this);
		return *this;
	}

	template<class T>
	oarchive& operator&(T& val)
	{
		operator<<(val);
		return *this;
	}

private:
	template<class T>
	iarchive& operator>>(T& val);
};

} // end of namespace gtl